﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/27 16:17:13
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using ImageK.Measure;
using ImageK.Process;

namespace ImageK.Plugin
{
    /// <summary>
    /// This plugin implements the Image/Color/Split Channels command.
    /// </summary>
    public class ChannelSplitter:PlugIn
    {
		public void run(String arg)
		{
			ImagePlus imp = IJ.getImage();
			if (imp.isComposite())
			{
				int z = imp.getSlice();
				int t = imp.getFrame();
				ImagePlus[] channels = split(imp);
				imp.changes = false;
				imp.setIgnoreFlush(true);
				imp.close();
				for (int i = 0; i < channels.Length; i++)
				{
					channels[i].setIJMenuBar(i == channels.Length - 1);
					channels[i].Show();
					if (z > 1 || t > 1)
						channels[i].setPosition(1, z, t);
				}
			}
			else if (imp.getType() == ImagePlus.COLOR_RGB)
				splitRGB(imp);
			else
				IJ.error("Split Channels", "Multichannel image required");
		}

		private void splitRGB(ImagePlus imp)
		{
			bool keepSource = IJ.altKeyDown();
			string title = imp.getTitle();
			Calibration cal = imp.getCalibration();
			int pos = imp.getCurrentSlice();
			ImageStack[] channels = splitRGB(imp.getStack(), keepSource);
			if (!keepSource)
			{ imp.Unlock(); imp.changes = false; imp.close(); }
			ImagePlus rImp = new ImagePlus(title + " (red)", channels[0]);
			rImp.setCalibration(cal);
			rImp.setIJMenuBar(false);
			rImp.Show();
			rImp.setSlice(pos);
			if (IJ.isMacOSX()) IJ.wait(500);
			ImagePlus gImp = new ImagePlus(title + " (green)", channels[1]);
			gImp.setCalibration(cal);
			gImp.setIJMenuBar(false);
			gImp.Show();
			gImp.setSlice(pos);
			if (IJ.isMacOSX()) IJ.wait(500);
			ImagePlus bImp = new ImagePlus(title + " (blue)", channels[2]);
			bImp.setCalibration(cal);
			bImp.Show();
			bImp.setSlice(pos);
		}

		/** Splits the specified image into separate channels. */
		public static ImagePlus[] split(ImagePlus imp)
		{
			if (imp.getType() == ImagePlus.COLOR_RGB)
			{
				ImageStack[] stacks = splitRGB(imp.getStack(), true);
				ImagePlus[] colorImages = new ImagePlus[3];
                colorImages[0] = new ImagePlus("red", stacks[0]);
                colorImages[1] = new ImagePlus("green", stacks[1]);
                colorImages[2] = new ImagePlus("blue", stacks[2]);
				return colorImages;
			}
			int width = imp.getWidth();
			int height = imp.getHeight();
			int channels = imp.getNChannels();
			int slices = imp.getNSlices();
			int frames = imp.getNFrames();
			int bitDepth = imp.getBitDepth();
			int size = slices * frames;
			ArrayList images = new ArrayList();
			HyperStackReducer reducer = new HyperStackReducer(imp);
			for (int c = 1; c <= channels; c++)
			{
				//todo:
				// ImageStack stack2 = new ImageStack(width, height, size); // create empty stack
				// stack2.setPixels(imp.getProcessor().getPixels(), 1); // can't create ImagePlus will null 1st image
				// ImagePlus imp2 = new ImagePlus("C" + c + "-" + imp.getTitle(), stack2);
				// stack2.setPixels(null, 1);
				// imp.setPosition(c, 1, 1);
				// imp2.setDimensions(1, slices, frames);
				// imp2.setCalibration(imp.getCalibration());
				// reducer.reduce(imp2);
				// if (imp.isComposite() && ((CompositeImage)imp).getMode() == IJ.GRAYSCALE)
				// 	IJ.run(imp2, "Grays", "");
				// if (imp2.getNDimensions() > 3)
				// 	imp2.setOpenAsHyperStack(true);
				// images.Add(imp2);
			}
            return (ImagePlus[])images.ToArray();
        }

		/** Returns, as an ImageStack, the specified channel, where 'c' must be greater 
			than zero and less than or equal to the number of channels in the image. */
		public static ImageStack getChannel(ImagePlus imp, int c)
		{
			if (imp.getBitDepth() == 24)
			{ // RGB?
				if (c < 1 || c > 3)
					throw new ArgumentException("Channel must be 1,2 or 3");
				ImageStack[] channels = splitRGB(imp.getStack(), true);
				return channels[c - 1];
			}
			if (c < 1 || c > imp.getNChannels())
				throw new ArgumentException("Channel less than 1 or greater than " + imp.getNChannels());
			ImageStack stack1 = imp.getStack();
			ImageStack stack2 = new ImageStack(imp.getWidth(), imp.getHeight());
			for (int t = 1; t <= imp.getNFrames(); t++)
			{
				for (int z = 1; z <= imp.getNSlices(); z++)
				{
					int n = imp.getStackIndex(c, z, t);
					stack2.addSlice(stack1.getProcessor(n));
				}
			}
			return stack2;
		}

		/** Splits the specified RGB stack into three 8-bit grayscale stacks. 
			Deletes the source stack if keepSource is false. */
		public static ImageStack[] splitRGB(ImageStack rgb, bool keepSource)
		{
			int w = rgb.getWidth();
			int h = rgb.getHeight();
			ImageStack[] channels = new ImageStack[3];
			for (int i = 0; i < 3; i++)
				channels[i] = new ImageStack(w, h);
			byte[] r, g, b;
			ColorProcessor cp;
			int slice = 1;
			int inc = keepSource ? 1 : 0;
			int n = rgb.getSize();
			for (int i = 1; i <= n; i++)
			{
				IJ.ShowStatus(i + "/" + n);
				r = new byte[w * h];
				g = new byte[w * h];
				b = new byte[w * h];
				cp = (ColorProcessor)rgb.getProcessor(slice);
				slice += inc;
				cp.getRGB(r, g, b);
				if (!keepSource)
					rgb.deleteSlice(1);
				channels[0].addSlice(null, r);
				channels[1].addSlice(null, g);
				channels[2].addSlice(null, b);
				IJ.showProgress((double)i / n);
			}
			return channels;
		}
	}
}
